08/05/2020
library(tidyr)
Attaching package: 㤼㸱tidyr㤼㸲
The following object is masked from 㤼㸱package:magrittr㤼㸲:
extract
wd <- getwd()
path1 <- file.path(wd, "Homo Deus_ A Brief History of T - Yuval Noah Harari.txt")
path2 <- file.path(wd,"Sapiens_ A Brief History of Hum - Yuval Noah Harari.txt")
path3 <- file.path(wd, "21 Lessons for the 21st Century - Yuval Noah Harari.txt")
homo <- readtext(path1)
sapiens <- readtext(path2)
lessons <- readtext(path3)
yuval_books <- bind_rows(homo, sapiens, lessons)
yuval_books %<>% mutate(book = case_when(doc_id == "Homo Deus_ A Brief History of T - Yuval Noah Harari.txt" ~ "homo_dues",
doc_id == "Sapiens_ A Brief History of Hum - Yuval Noah Harari.txt" ~ "sapiens",
doc_id == "21 Lessons for the 21st Century - Yuval Noah Harari.txt" ~ "21_lessons")) %>%
select(-doc_id)
head(yuval_books)
NA
yuval_bigram <- yuval_books %>% unnest_tokens(bigram, text, token = "ngrams", n = 2)
bigram_separated <- yuval_bigram %>% separate(bigram, c("word1", "word2"), sep = " ")
bigram_filtered <- bigram_separated %>% filter(!word1 %in% stop_words$word) %>%
filter(!word2 %in% stop_words$word)
bigram_counted <- bigram_filtered %>%
count(word1, word2, sort = TRUE)
bigram_counted
bigram_united <- bigram_filtered %>% unite(bigram, word1, word2, sep = " ")
bigram_united
trigram_filtered <- yuval_books %>%
unnest_tokens(trigram, text, token = "ngrams", n = 3) %>%
separate(trigram, c("word1", "word2", "word3"), sep = " ") %>%
filter(!word1 %in% stop_words$word,
!word2 %in% stop_words$word,
!word3 %in% stop_words$word)
trigram_filtered %>% count(word1, word2, word3, sort = TRUE)
trigram_united <- trigram_filtered %>% unite(trigram, word1, word2, word3, sep = " ")
trigram_united
trigram_filtered %>%
filter(word2 == "future") %>%
count(book, word1, sort = TRUE)
trigram_filtered %>%
filter(word2 == "past") %>%
count(book, word1, sort = TRUE)
monogram_filtered <- yuval_books %>%
unnest_tokens(monogram, text, token = "ngrams", n = 1) %>%
filter(!monogram %in% stop_words$word)
monogram_filtered %>%
filter(monogram == "future") %>%
count(book, monogram, sort = TRUE)
bigram_tf_idf <- bigram_united %>%
count(book, bigram) %>%
bind_tf_idf(bigram, book, n) %>%
arrange(desc(tf_idf))
bigram_tf_idf
trigram_tf_idf <- trigram_united %>%
count(book, trigram) %>%
bind_tf_idf(trigram, book, n) %>%
arrange(desc(tf_idf))
trigram_tf_idf
bigram_separated %>%
filter(word1 == "not") %>%
count(word1, word2, sort = TRUE)
AFINN <- get_sentiments("afinn")
AFINN
not_words <- bigram_separated %>%
filter(word1 == "not") %>%
inner_join(AFINN, by = c(word2 = "word")) %>%
count(word2, value, sort = TRUE)
not_words
library(ggplot2)
not_words %>%
mutate(contribution = n * value) %>%
arrange(desc(abs(contribution))) %>%
head(20) %>%
mutate(word2 = reorder(word2, contribution)) %>%
ggplot(aes(word2, n * value, fill = n * value > 0)) +
geom_col(show.legend = FALSE) +
xlab("Words preceded by \"not\"") +
ylab("Sentiment value * number of occurrences") +
coord_flip()

# filter for only relatively common combinations
bigram_graph <- bigram_counted %>%
filter(n > 20) %>%
graph_from_data_frame()
bigram_graph
IGRAPH 56d3872 DN-- 74 48 --
+ attr: name (v/c), n (e/n)
+ edges from 56d3872 (vertex names):
[1] homo ->sapiens twentieth ->century agricultural->revolution human ->rights nineteenth ->century
[6] data ->processing economic ->growth middle ->east natural ->selection world ->war
[11] hunter ->gatherers free ->market industrial ->revolution hunter ->gatherer global ->warming
[16] artificial ->intelligence cognitive ->revolution million ->people entire ->world human ->species
[21] stone ->age modern ->science world ->view scientific ->revolution climate ->change
[26] science ->fiction subjective ->experiences famine ->plague human ->brain vast ->majority
[31] liberal ->story roman ->empire secular ->people job ->market human ->life
[36] raw ->materials genetic ->engineering scientific ->research â ->â ancient ->foragers
+ ... omitted several edges
library(ggraph)
package 㤼㸱ggraph㤼㸲 was built under R version 3.6.3
set.seed(2017)
ggraph(bigram_graph, layout = "fr") +
geom_edge_link() +
geom_node_point() +
geom_node_text(aes(label = name), vjust = 1, hjust = 1)

set.seed(2016)
a <- grid::arrow(type = "closed", length = unit(.15, "inches"))
ggraph(bigram_graph, layout = "fr") +
geom_edge_link(aes(edge_alpha = n), show.legend = FALSE,
arrow = a, end_cap = circle(.07, 'inches')) +
geom_node_point(color = "lightblue", size = 5) +
geom_node_text(aes(label = name), vjust = 1, hjust = 1) +
theme_void()

| 29/04/2020 - 07/05/2020 |
|
|
|
|
|
|
|
r library(quanteda) library(readtext) library(tidytext) library(tidyverse) |
|
|
|
|
|
|
| ```r #path1 <- “C:\Users\E7470\Calibre Library\Yuval Noah Harari\Homo Deus_ A Brief History of Tomor (663)\Homo Deus_ A Brief History of T - Yuval Noah Harari.txt” #path2 <- “C:\Users\E7470\Calibre Library\Yuval Noah Harari\Sapiens_ A Brief History of Humanki (353)\Sapiens_ A Brief History of Hum - Yuval Noah Harari.txt” #path3 <- “C:\Users\E7470\Calibre Library\Yuval Noah Harari\21 Lessons for the 21st Century (664)\21 Lessons for the 21st Century - Yuval Noah Harari.txt” |
| path1 <- “C:\Tran Phu Hoa workspace\1. Current\1_NLP\Natural-Language-Processing\Homo Deus_ A Brief History of T - Yuval Noah Harari.txt” path2 <- “C:\Tran Phu Hoa workspace\1. Current\1_NLP\Natural-Language-Processing\Sapiens_ A Brief History of Hum - Yuval Noah Harari.txt” path3 <- “C:\Tran Phu Hoa workspace\1. Current\1_NLP\Natural-Language-Processing\21 Lessons for the 21st Century - Yuval Noah Harari.txt” |
| homo_deus <- readtext(path1) sapiens <- readtext(path2) lessons <- readtext(path3) |
| yuval_books <- bind_rows(homo_deus, sapiens, lessons) |
| ``` |
|
|
|
|
|
|
r tidy_yuval <- yuval_books %>% unnest_tokens(word,text) |
|
|
|
|
|
|
r tidy_yuval %>% anti_join(stop_words) %>% count(doc_id,word, sort = TRUE) %>% bind_tf_idf(word,doc_id,n) %>% select(-doc_id) %>% arrange(desc(tf_idf)) |
|
|
|
|
|
|
| ```r #mystopwords <- tibble(word = c(“www.â”, “2017”, “2016”, “cortã”, “2015”, “bn”, “file”, “cg”, “cb”, “cm”, “ab”, "_k“,”k“,”_x")) |
| #physics_words <- anti_join(physics_words, mystopwords, by = “word”) |
| plot_yuval <- tidy_yuval %>% anti_join(stop_words) %>% count(doc_id,word, sort = TRUE) %>% bind_tf_idf(word,doc_id,n) %>% mutate(word = str_remove_all(word, “")) %>% group_by(doc_id) %>% top_n(15, tf_idf) %>% ungroup() %>% mutate(word = reorder_within(word, tf_idf, doc_id)) %>% mutate(author = factor(doc_id, levels = c("Homo Deus A Brief History of T - Yuval Noah Harari.txt”, “21 Lessons for the 21st Century - Yuval Noah Harari.txt”, “Sapiens_ A Brief History of Hum - Yuval Noah Harari.txt”))) |
| ggplot(plot_yuval, aes(word, tf_idf, fill = doc_id)) + geom_col(show.legend = FALSE) + labs(x = NULL, y = “tf-idf”) + facet_wrap(~doc_id, ncol = 2, scales = “free”) + coord_flip() + scale_x_reordered() ``` |
|
|
|
|
|
|
r yuval_books %>% filter(str_detect(text, "2017")) %>% select(text) |
|
|
|
|
|
|
| ```r library(forcats) |
| plot_physics <- physics_words %>% bind_tf_idf(word, author, n) %>% mutate(word = fct_reorder(word, tf_idf)) %>% mutate(author = factor(author, levels = c(“Galilei, Galileo”, “Huygens, Christiaan”, “Tesla, Nikola”, “Einstein, Albert”))) ``` |
|
|
|
|
|
|
| ```r |
| plot_physics %>% group_by(author) %>% top_n(15, tf_idf) %>% ungroup() %>% mutate(word = reorder(word, tf_idf)) %>% ggplot(aes(word, tf_idf, fill = author)) + geom_col(show.legend = FALSE) + labs(x = NULL, y = “tf-idf”) + facet_wrap(~author, ncol = 2, scales = “free”) + coord_flip() ``` |
|
|
|
28/04/2020
Chapter 3 We want to answer how to quantify what a document is about
To answer these questions we test this method: tf = term frequency idf = inverse document frequency = decreases the weight for commonly used words and increases the weight for words that are not used very much in a collection of documents
tf - idf = The statistic tf-idf is intended to measure how important a word is to a document in a collection (or corpus) of documents, for example, to one novel in a collection of novels or to one website in a collection of websites.
library(tidyverse)
library(janeaustenr)
library(tidytext)
library(textdata)
book_words <- austen_books() %>%
unnest_tokens(word, text) %>%
count(book, word, sort = TRUE)
total_words <- book_words %>%
group_by(book) %>%
summarize(total = sum(n))
book_words <- left_join(book_words, total_words)
book_words
Zipf’s law states that the frequency that a word appears is inversely proportional to its rank.
freq_by_rank <- book_words %>%
group_by(book) %>%
mutate(rank = row_number(),
`term frequency` = n/total)
freq_by_rank
freq_by_rank %>%
ggplot(aes(rank, `term frequency`, color = book)) +
geom_line(size = 1.1, alpha = 0.8, show.legend = FALSE) +
scale_x_log10() +
scale_y_log10()
rank_subset <- freq_by_rank %>%
filter(rank < 500,
rank > 10)
lm(log10(`term frequency`) ~ log10(rank), data = rank_subset)
freq_by_rank %>%
ggplot(aes(rank, `term frequency`, color = book)) +
geom_abline(intercept = -0.62, slope = -1.1, color = "gray50", linetype = 2) +
geom_line(size = 1.1, alpha = 0.8, show.legend = FALSE) +
scale_x_log10() +
scale_y_log10()
The idea of tf-idf is to find the important words for the content of each document by decreasing the weight for commonly used words and increasing the weight for words that are not used very much in a collection or corpus of documents, in this case, the group of Jane Austen’s novels as a whole. Calculating tf-idf attempts to find the words that are important (i.e., common) in a text, but not too common
book_words <- book_words %>%
bind_tf_idf(word, book, n)
book_words
book_words %>%
select(-total) %>%
arrange(desc(tf_idf))
book_words %>%
arrange(desc(tf_idf)) %>%
mutate(word = factor(word, levels = rev(unique(word))))
book_words %>%
arrange(desc(tf_idf)) %>%
mutate(word = factor(word, levels = rev(unique(word)))) %>%
group_by(book) %>%
top_n(15) %>%
ungroup()
book_words %>%
arrange(desc(tf_idf)) %>%
mutate(word = factor(word, levels = rev(unique(word)))) %>%
group_by(book) %>%
top_n(15) %>%
ungroup() %>%
ggplot(aes(word, tf_idf, fill = book)) +
geom_col(show.legend = FALSE) +
labs(x = NULL, y = "tf-idf") +
facet_wrap(~book, ncol = 2, scales = "free") +
coord_flip()
Still all proper nouns in Figure 3.4! These words are, as measured by tf-idf, the most important to each novel and most readers would likely agree. What measuring tf-idf has done here is show us that Jane Austen used similar language across her six novels, and what distinguishes one novel from the rest within the collection of her works are the proper nouns, the names of people and places. This is the point of tf-idf; it identifies words that are important to one document within a collection of documents.
suppressMessages({
if(!require(gutenbergr))
install.packages("gutenbergr", repos = "http://cran.us.r-project.org")
library(gutenbergr)
})
physics <- gutenberg_download(c(37729, 14725, 13476, 30155),
meta_fields = "author")
To calculate and visulize relationships between words in my text dataset
Tokenizing by n-gram
We do this by adding the token = “ngrams” option to unnest_tokens(), and setting n to the number of words we wish to capture in each n-gram. When we set n to 2, we are examining pairs of two consecutive words, often called “bigrams”:
austen_bigrams <- austen_books() %>%
unnest_tokens(bigram, text, token = "ngrams", n = 2)
austen_bigrams
Counting and filtering n-grams
austen_bigrams %>%
count(bigram, sort = TRUE)
As one might expect, a lot of the most common bigrams are pairs of common (uninteresting) words, such as of the and to be: what we call “stop-words” (see Chapter 1). This is a useful time to use tidyr’s separate(), which splits a column into multiple based on a delimiter. This lets us separate it into two columns, “word1” and “word2”, at which point we can remove cases where either is a stop-word.
bigrams_separated <- austen_bigrams %>%
separate(bigram, c("word1", "word2"), sep = " ")
bigrams_filtered <- bigrams_separated %>%
filter(!word1 %in% stop_words$word) %>%
filter(!word2 %in% stop_words$word)
# new bigram counts:
bigram_counts <- bigrams_filtered %>%
count(word1, word2, sort = TRUE)
bigram_counts
bigrams_united <- bigrams_filtered %>%
unite(bigram, word1, word2, sep = " ")
bigrams_united
In other analyses you may be interested in the most common trigrams, which are consecutive sequences of 3 words. We can find this by setting n = 3:
austen_books() %>%
unnest_tokens(trigram, text, token = "ngrams", n = 3) %>%
separate(trigram, c("word1", "word2", "word3"), sep = " ") %>%
filter(!word1 %in% stop_words$word,
!word2 %in% stop_words$word,
!word3 %in% stop_words$word) %>%
count(word1, word2, word3, sort = TRUE)
bigrams_filtered %>%
filter(word2 == "street") %>%
count(book, word1, sort = TRUE)
A bigram can also be treated as a term in a document in the same way that we treated individual words. For example, we can look at the tf-idf (Chapter 3) of bigrams across Austen novels. These tf-idf values can be visualized within each book, just as we did for words (Figure 4.1).
bigram_tf_idf <- bigrams_united %>%
count(book, bigram) %>%
bind_tf_idf(bigram, book, n) %>%
arrange(desc(tf_idf))
bigram_tf_idf
not_words <- bigrams_separated %>%
filter(word1 == "not") %>%
inner_join(AFINN, by = c(word2 = "word")) %>%
count(word2, value, sort = TRUE)
not_words
not_words %>%
mutate(contribution = n * value) %>%
arrange(desc(abs(contribution))) %>%
head(20) %>%
mutate(word2 = reorder(word2, contribution)) %>%
ggplot(aes(word2, n * value, fill = n * value > 0)) +
geom_col(show.legend = FALSE) +
xlab("Words preceded by \"not\"") +
ylab("Sentiment value * number of occurrences") +
coord_flip()
negation_words <- c("not", "no", "never", "without")
AFINN <- get_sentiments("afinn")
negated_words <- bigrams_separated %>%
filter(word1 %in% negation_words) %>%
inner_join(AFINN, by = c(word2 = "word")) %>%
count(word1, word2, value, sort = TRUE)
negated_words
negated_words %>% select(word1) %>% distinct()
negated_words %>%
mutate(word1 = factor(word1, levels = unique(word1))) %>%
group_by(word1) %>%
top_n(12) %>%
ungroup() %>%
mutate(contribution = n * value) %>%
mutate(word2 = reorder_within(word2, contribution, word1)) %>%
ggplot(aes(word2, contribution, fill = contribution > 0)) +
geom_col(show.legend = FALSE) +
xlab("Words preceded by \"not\"") +
ylab("Sentiment value * number of occurrences") +
facet_wrap(~ word1, ncol = 2, scales = "free", dir = "v") +
coord_flip() +
scale_x_reordered()
20/04/2020
suppressMessages({
if(!require(topicmodels))
install.packages("topicmodels", repos = "http://cran.us.r-project.org")
library(topicmodels)
})
data("AssociatedPress")
AssociatedPress
# set a seed so that the output of the model is predictable
ap_lda <- LDA(AssociatedPress, k = 2, control = list(seed = 1234))
ap_lda
suppressMessages({
if(!require(tidytext))
install.packages("tidytext", repos = "http://cran.us.r-project.org")
library(tidytext)
})
ap_topics <- tidy(ap_lda, matrix = "beta")
ap_topics
ap_top_terms <- ap_topics %>% group_by(topic) %>% top_n(10, beta) %>% ungroup() %>% arrange(topic, -beta)
ap_top_terms %>% mutate(term = reorder_within(term, beta, topic)) %>% ggplot(aes(term, beta, fill = factor(topic))) + geom_col(show.legend = FALSE) + facet_wrap(~ topic, scales = “free”) + coord_flip() + scale_x_reordered()
library(ggplot2)
library(dplyr)
ap_top_terms <- ap_topics %>%
group_by(topic) %>%
top_n(10,beta) %>%
ungroup() %>%
arrange(topic, -beta)
ap_top_terms
ap_top_terms %>%
mutate(term = reorder_within(term, beta, topic)) %>%
ggplot(aes(term, beta, fill = factor(topic))) +
geom_col(show.legend = FALSE) +
facet_wrap(~ topic, scales = "free") +
coord_flip() +
scale_x_reordered()
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KMDgvMDUvMjAyMA0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHl0ZXh0KQ0KbGlicmFyeShyZWFkdGV4dCkNCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KHRpZHlyKQ0KDQpgYGANCg0KYGBge3J9DQp3ZCA8LSBnZXR3ZCgpDQpwYXRoMSA8LSBmaWxlLnBhdGgod2QsICJIb21vIERldXNfIEEgQnJpZWYgSGlzdG9yeSBvZiBUIC0gWXV2YWwgTm9haCBIYXJhcmkudHh0IikNCnBhdGgyIDwtIGZpbGUucGF0aCh3ZCwiU2FwaWVuc18gQSBCcmllZiBIaXN0b3J5IG9mIEh1bSAtIFl1dmFsIE5vYWggSGFyYXJpLnR4dCIpDQpwYXRoMyA8LSBmaWxlLnBhdGgod2QsICIyMSBMZXNzb25zIGZvciB0aGUgMjFzdCBDZW50dXJ5IC0gWXV2YWwgTm9haCBIYXJhcmkudHh0IikNCg0KaG9tbyA8LSByZWFkdGV4dChwYXRoMSkgIA0Kc2FwaWVucyA8LSByZWFkdGV4dChwYXRoMikNCmxlc3NvbnMgPC0gcmVhZHRleHQocGF0aDMpDQoNCnl1dmFsX2Jvb2tzIDwtIGJpbmRfcm93cyhob21vLCBzYXBpZW5zLCBsZXNzb25zKQ0KeXV2YWxfYm9va3MgJTw+JSBtdXRhdGUoYm9vayA9IGNhc2Vfd2hlbihkb2NfaWQgPT0gIkhvbW8gRGV1c18gQSBCcmllZiBIaXN0b3J5IG9mIFQgLSBZdXZhbCBOb2FoIEhhcmFyaS50eHQiIH4gImhvbW9fZHVlcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvY19pZCA9PSAiU2FwaWVuc18gQSBCcmllZiBIaXN0b3J5IG9mIEh1bSAtIFl1dmFsIE5vYWggSGFyYXJpLnR4dCIgfiAic2FwaWVucyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvY19pZCA9PSAiMjEgTGVzc29ucyBmb3IgdGhlIDIxc3QgQ2VudHVyeSAtIFl1dmFsIE5vYWggSGFyYXJpLnR4dCIgfiAiMjFfbGVzc29ucyIpKSAlPiUNCiAgc2VsZWN0KC1kb2NfaWQpDQogIA0KDQpoZWFkKHl1dmFsX2Jvb2tzKQ0KDQpgYGANCg0KYGBge3J9DQp5dXZhbF9iaWdyYW0gPC0geXV2YWxfYm9va3MgJT4lIHVubmVzdF90b2tlbnMoYmlncmFtLCB0ZXh0LCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMikNCmBgYA0KDQpgYGB7cn0NCmhlYWQoeXV2YWxfYmlncmFtKQ0KdGFpbCh5dXZhbF9iaWdyYW0pDQpgYGANCg0KYGBge3J9DQp5dXZhbF9iaWdyYW0gJT4lIGNvdW50KGJpZ3JhbSwgc29ydCA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQpiaWdyYW1fc2VwYXJhdGVkIDwtIHl1dmFsX2JpZ3JhbSAlPiUgc2VwYXJhdGUoYmlncmFtLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpDQoNCmJpZ3JhbV9maWx0ZXJlZCA8LSBiaWdyYW1fc2VwYXJhdGVkICU+JSBmaWx0ZXIoIXdvcmQxICVpbiUgc3RvcF93b3JkcyR3b3JkKSAlPiUNCiAgZmlsdGVyKCF3b3JkMiAlaW4lIHN0b3Bfd29yZHMkd29yZCkNCg0KYmlncmFtX2NvdW50ZWQgPC0gYmlncmFtX2ZpbHRlcmVkICU+JQ0KICBjb3VudCh3b3JkMSwgd29yZDIsIHNvcnQgPSBUUlVFKQ0KYmlncmFtX2NvdW50ZWQNCmBgYA0KDQpgYGB7cn0NCmJpZ3JhbV91bml0ZWQgPC0gYmlncmFtX2ZpbHRlcmVkICU+JSB1bml0ZShiaWdyYW0sIHdvcmQxLCB3b3JkMiwgc2VwID0gIiAiKQ0KYmlncmFtX3VuaXRlZA0KYGBgDQoNCmBgYHtyfQ0KdHJpZ3JhbV9maWx0ZXJlZCA8LSB5dXZhbF9ib29rcyAlPiUNCiAgdW5uZXN0X3Rva2Vucyh0cmlncmFtLCB0ZXh0LCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMykgJT4lDQogIHNlcGFyYXRlKHRyaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiwgIndvcmQzIiksIHNlcCA9ICIgIikgJT4lDQogIGZpbHRlcighd29yZDEgJWluJSBzdG9wX3dvcmRzJHdvcmQsDQogICAgICAgICAhd29yZDIgJWluJSBzdG9wX3dvcmRzJHdvcmQsDQogICAgICAgICAhd29yZDMgJWluJSBzdG9wX3dvcmRzJHdvcmQpDQoNCnRyaWdyYW1fZmlsdGVyZWQgJT4lIGNvdW50KHdvcmQxLCB3b3JkMiwgd29yZDMsIHNvcnQgPSBUUlVFKQ0KDQp0cmlncmFtX3VuaXRlZCA8LSB0cmlncmFtX2ZpbHRlcmVkICU+JSB1bml0ZSh0cmlncmFtLCB3b3JkMSwgd29yZDIsIHdvcmQzLCBzZXAgPSAiICIpDQoNCnRyaWdyYW1fdW5pdGVkDQpgYGANCg0KYGBge3J9DQp0cmlncmFtX2ZpbHRlcmVkICU+JQ0KICBmaWx0ZXIod29yZDIgPT0gImZ1dHVyZSIpICU+JQ0KICBjb3VudChib29rLCB3b3JkMSwgc29ydCA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQp0cmlncmFtX2ZpbHRlcmVkICU+JQ0KICBmaWx0ZXIod29yZDIgPT0gInByZXNlbnQiKSAlPiUNCiAgY291bnQoYm9vaywgd29yZDEsIHNvcnQgPSBUUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KbW9ub2dyYW1fZmlsdGVyZWQgPC0geXV2YWxfYm9va3MgJT4lDQogIHVubmVzdF90b2tlbnMobW9ub2dyYW0sIHRleHQsIHRva2VuID0gIm5ncmFtcyIsIG4gPSAxKSAlPiUNCiAgZmlsdGVyKCFtb25vZ3JhbSAlaW4lIHN0b3Bfd29yZHMkd29yZCkNCmBgYA0KDQpgYGB7cn0NCm1vbm9ncmFtX2ZpbHRlcmVkICU+JQ0KICBmaWx0ZXIobW9ub2dyYW0gPT0gImZ1dHVyZSIpICU+JQ0KICBjb3VudChib29rLCBtb25vZ3JhbSwgc29ydCA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQpiaWdyYW1fdGZfaWRmIDwtIGJpZ3JhbV91bml0ZWQgJT4lDQogIGNvdW50KGJvb2ssIGJpZ3JhbSkgJT4lDQogIGJpbmRfdGZfaWRmKGJpZ3JhbSwgYm9vaywgbikgJT4lDQogIGFycmFuZ2UoZGVzYyh0Zl9pZGYpKQ0KDQpiaWdyYW1fdGZfaWRmDQpgYGANCg0KYGBge3J9DQp0cmlncmFtX3RmX2lkZiA8LSB0cmlncmFtX3VuaXRlZCAlPiUNCiAgY291bnQoYm9vaywgdHJpZ3JhbSkgJT4lDQogIGJpbmRfdGZfaWRmKHRyaWdyYW0sIGJvb2ssIG4pICU+JQ0KICBhcnJhbmdlKGRlc2ModGZfaWRmKSkNCg0KdHJpZ3JhbV90Zl9pZGYNCmBgYA0KDQpgYGB7cn0NCmJpZ3JhbV9zZXBhcmF0ZWQgJT4lDQogIGZpbHRlcih3b3JkMSA9PSAibm90IikgJT4lDQogIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpDQpgYGANCmBgYHtyfQ0KQUZJTk4gPC0gZ2V0X3NlbnRpbWVudHMoImFmaW5uIikNCg0KQUZJTk4NCmBgYA0KDQpgYGB7cn0NCm5vdF93b3JkcyA8LSBiaWdyYW1fc2VwYXJhdGVkICU+JQ0KICBmaWx0ZXIod29yZDEgPT0gIm5vdCIpICU+JQ0KICBpbm5lcl9qb2luKEFGSU5OLCBieSA9IGMod29yZDIgPSAid29yZCIpKSAlPiUNCiAgY291bnQod29yZDIsIHZhbHVlLCBzb3J0ID0gVFJVRSkNCg0Kbm90X3dvcmRzDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCm5vdF93b3JkcyAlPiUNCiAgbXV0YXRlKGNvbnRyaWJ1dGlvbiA9IG4gKiB2YWx1ZSkgJT4lDQogIGFycmFuZ2UoZGVzYyhhYnMoY29udHJpYnV0aW9uKSkpICU+JQ0KICBoZWFkKDIwKSAlPiUNCiAgbXV0YXRlKHdvcmQyID0gcmVvcmRlcih3b3JkMiwgY29udHJpYnV0aW9uKSkgJT4lDQogIGdncGxvdChhZXMod29yZDIsIG4gKiB2YWx1ZSwgZmlsbCA9IG4gKiB2YWx1ZSA+IDApKSArDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgeGxhYigiV29yZHMgcHJlY2VkZWQgYnkgXCJub3RcIiIpICsNCiAgeWxhYigiU2VudGltZW50IHZhbHVlICogbnVtYmVyIG9mIG9jY3VycmVuY2VzIikgKw0KICBjb29yZF9mbGlwKCkNCmBgYA0KDQpgYGB7cn0NCm5lZ2F0aW9uX3dvcmRzIDwtIGMoIm5vdCIsICJubyIsICJuZXZlciIsICJ3aXRob3V0IikNCg0KbmVnYXRlZF93b3JkcyA8LSBiaWdyYW1fc2VwYXJhdGVkICU+JQ0KICBmaWx0ZXIod29yZDEgJWluJSBuZWdhdGlvbl93b3JkcykgJT4lDQogIGlubmVyX2pvaW4oQUZJTk4sIGJ5ID0gYyh3b3JkMiA9ICJ3b3JkIikpICU+JQ0KICBjb3VudCh3b3JkMSwgd29yZDIsIHZhbHVlLCBzb3J0ID0gVFJVRSkNCg0KbmVnYXRlZF93b3Jkcw0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShpZ3JhcGgpDQoNCiMgb3JpZ2luYWwgY291bnRzDQpiaWdyYW1fY291bnRlZA0KYGBgDQoNCmBgYHtyfQ0KIyBmaWx0ZXIgZm9yIG9ubHkgcmVsYXRpdmVseSBjb21tb24gY29tYmluYXRpb25zDQpiaWdyYW1fZ3JhcGggPC0gYmlncmFtX2NvdW50ZWQgJT4lDQogIGZpbHRlcihuID4gMjApICU+JQ0KICBncmFwaF9mcm9tX2RhdGFfZnJhbWUoKQ0KDQpiaWdyYW1fZ3JhcGgNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dyYXBoKQ0Kc2V0LnNlZWQoMjAxNykNCg0KZ2dyYXBoKGJpZ3JhbV9ncmFwaCwgbGF5b3V0ID0gImZyIikgKw0KICBnZW9tX2VkZ2VfbGluaygpICsNCiAgZ2VvbV9ub2RlX3BvaW50KCkgKw0KICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgdmp1c3QgPSAxLCBoanVzdCA9IDEpDQpgYGANCg0KYGBge3J9DQpzZXQuc2VlZCgyMDE2KQ0KDQphIDwtIGdyaWQ6OmFycm93KHR5cGUgPSAiY2xvc2VkIiwgbGVuZ3RoID0gdW5pdCguMTUsICJpbmNoZXMiKSkNCg0KZ2dyYXBoKGJpZ3JhbV9ncmFwaCwgbGF5b3V0ID0gImZyIikgKw0KICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IG4pLCBzaG93LmxlZ2VuZCA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICBhcnJvdyA9IGEsIGVuZF9jYXAgPSBjaXJjbGUoLjA3LCAnaW5jaGVzJykpICsNCiAgZ2VvbV9ub2RlX3BvaW50KGNvbG9yID0gImxpZ2h0Ymx1ZSIsIHNpemUgPSA1KSArDQogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCB2anVzdCA9IDEsIGhqdXN0ID0gMSkgKw0KICB0aGVtZV92b2lkKCkNCmBgYA0KDQotLS0tLQ0KMjkvMDQvMjAyMCAtIDA3LzA1LzIwMjANCg0KYGBge3J9DQoNCmBgYA0KDQoNCmBgYHtyfQ0KbGlicmFyeShxdWFudGVkYSkNCmxpYnJhcnkocmVhZHRleHQpDQpsaWJyYXJ5KHRpZHl0ZXh0KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpgYGANCg0KDQoNCmBgYHtyfQ0KI3BhdGgxIDwtICJDOlxcVXNlcnNcXEU3NDcwXFxDYWxpYnJlIExpYnJhcnlcXFl1dmFsIE5vYWggSGFyYXJpXFxIb21vIERldXNfIEEgQnJpZWYgSGlzdG9yeSBvZiBUb21vciAoNjYzKVxcSG9tbyBEZXVzXyBBIEJyaWVmIEhpc3Rvcnkgb2YgVCAtIFl1dmFsIE5vYWggSGFyYXJpLnR4dCINCiNwYXRoMiA8LSAiQzpcXFVzZXJzXFxFNzQ3MFxcQ2FsaWJyZSBMaWJyYXJ5XFxZdXZhbCBOb2FoIEhhcmFyaVxcU2FwaWVuc18gQSBCcmllZiBIaXN0b3J5IG9mIEh1bWFua2kgKDM1MylcXFNhcGllbnNfIEEgQnJpZWYgSGlzdG9yeSBvZiBIdW0gLSBZdXZhbCBOb2FoIEhhcmFyaS50eHQiDQojcGF0aDMgPC0gIkM6XFxVc2Vyc1xcRTc0NzBcXENhbGlicmUgTGlicmFyeVxcWXV2YWwgTm9haCBIYXJhcmlcXDIxIExlc3NvbnMgZm9yIHRoZSAyMXN0IENlbnR1cnkgKDY2NClcXDIxIExlc3NvbnMgZm9yIHRoZSAyMXN0IENlbnR1cnkgLSBZdXZhbCBOb2FoIEhhcmFyaS50eHQiDQoNCnBhdGgxIDwtICJDOlxcVHJhbiBQaHUgSG9hIHdvcmtzcGFjZVxcMS4gQ3VycmVudFxcMV9OTFBcXE5hdHVyYWwtTGFuZ3VhZ2UtUHJvY2Vzc2luZ1xcSG9tbyBEZXVzXyBBIEJyaWVmIEhpc3Rvcnkgb2YgVCAtIFl1dmFsIE5vYWggSGFyYXJpLnR4dCINCnBhdGgyIDwtICJDOlxcVHJhbiBQaHUgSG9hIHdvcmtzcGFjZVxcMS4gQ3VycmVudFxcMV9OTFBcXE5hdHVyYWwtTGFuZ3VhZ2UtUHJvY2Vzc2luZ1xcU2FwaWVuc18gQSBCcmllZiBIaXN0b3J5IG9mIEh1bSAtIFl1dmFsIE5vYWggSGFyYXJpLnR4dCINCnBhdGgzIDwtICJDOlxcVHJhbiBQaHUgSG9hIHdvcmtzcGFjZVxcMS4gQ3VycmVudFxcMV9OTFBcXE5hdHVyYWwtTGFuZ3VhZ2UtUHJvY2Vzc2luZ1xcMjEgTGVzc29ucyBmb3IgdGhlIDIxc3QgQ2VudHVyeSAtIFl1dmFsIE5vYWggSGFyYXJpLnR4dCINCg0KaG9tb19kZXVzIDwtIHJlYWR0ZXh0KHBhdGgxKSAgDQpzYXBpZW5zIDwtIHJlYWR0ZXh0KHBhdGgyKQ0KbGVzc29ucyA8LSByZWFkdGV4dChwYXRoMykNCg0KeXV2YWxfYm9va3MgPC0gYmluZF9yb3dzKGhvbW9fZGV1cywgc2FwaWVucywgbGVzc29ucykNCg0KDQpgYGANCg0KYGBge3J9DQp0aWR5X3l1dmFsIDwtIHl1dmFsX2Jvb2tzICU+JSB1bm5lc3RfdG9rZW5zKHdvcmQsdGV4dCkNCmBgYA0KDQpgYGB7cn0NCnRpZHlfeXV2YWwgICU+JSANCiAgICBhbnRpX2pvaW4oc3RvcF93b3JkcykgJT4lIA0KICAgIGNvdW50KGRvY19pZCx3b3JkLCBzb3J0ID0gVFJVRSkgJT4lDQogICAgYmluZF90Zl9pZGYod29yZCxkb2NfaWQsbikgJT4lIA0KICAgIHNlbGVjdCgtZG9jX2lkKSAlPiUNCiAgICBhcnJhbmdlKGRlc2ModGZfaWRmKSkNCmBgYA0KDQpgYGB7cn0NCiNteXN0b3B3b3JkcyA8LSB0aWJibGUod29yZCA9IGMoInd3dy7DoiIsICIyMDE3IiwgIjIwMTYiLCAiY29ydMOjIiwgIjIwMTUiLCAiYm4iLCAiZmlsZSIsICJjZyIsICJjYiIsICJjbSIsICJhYiIsICJfayIsICJfa18iLCAiX3giKSkNCg0KI3BoeXNpY3Nfd29yZHMgPC0gYW50aV9qb2luKHBoeXNpY3Nfd29yZHMsIG15c3RvcHdvcmRzLCBieSA9ICJ3b3JkIikNCg0KcGxvdF95dXZhbCA8LSB0aWR5X3l1dmFsICU+JSANCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpICU+JSANCiAgY291bnQoZG9jX2lkLHdvcmQsIHNvcnQgPSBUUlVFKSAlPiUNCiAgYmluZF90Zl9pZGYod29yZCxkb2NfaWQsbikgJT4lDQogIG11dGF0ZSh3b3JkID0gc3RyX3JlbW92ZV9hbGwod29yZCwgIl8iKSkgJT4lDQogIGdyb3VwX2J5KGRvY19pZCkgJT4lIA0KICB0b3BfbigxNSwgdGZfaWRmKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBtdXRhdGUod29yZCA9IHJlb3JkZXJfd2l0aGluKHdvcmQsIHRmX2lkZiwgZG9jX2lkKSkgJT4lDQogIG11dGF0ZShhdXRob3IgPSBmYWN0b3IoZG9jX2lkLCBsZXZlbHMgPSBjKCJIb21vIERldXNfIEEgQnJpZWYgSGlzdG9yeSBvZiBUIC0gWXV2YWwgTm9haCBIYXJhcmkudHh0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIxIExlc3NvbnMgZm9yIHRoZSAyMXN0IENlbnR1cnkgLSBZdXZhbCBOb2FoIEhhcmFyaS50eHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2FwaWVuc18gQSBCcmllZiBIaXN0b3J5IG9mIEh1bSAtIFl1dmFsIE5vYWggSGFyYXJpLnR4dCIpKSkNCg0KZ2dwbG90KHBsb3RfeXV2YWwsIGFlcyh3b3JkLCB0Zl9pZGYsIGZpbGwgPSBkb2NfaWQpKSArDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgbGFicyh4ID0gTlVMTCwgeSA9ICJ0Zi1pZGYiKSArDQogIGZhY2V0X3dyYXAofmRvY19pZCwgbmNvbCA9IDIsIHNjYWxlcyA9ICJmcmVlIikgKw0KICBjb29yZF9mbGlwKCkgKw0KICBzY2FsZV94X3Jlb3JkZXJlZCgpDQpgYGANCg0KYGBge3J9DQp5dXZhbF9ib29rcyAlPiUgDQogIGZpbHRlcihzdHJfZGV0ZWN0KHRleHQsICIyMDE3IikpICU+JSANCiAgc2VsZWN0KHRleHQpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGZvcmNhdHMpDQoNCnBsb3RfcGh5c2ljcyA8LSBwaHlzaWNzX3dvcmRzICU+JQ0KICBiaW5kX3RmX2lkZih3b3JkLCBhdXRob3IsIG4pICU+JQ0KICBtdXRhdGUod29yZCA9IGZjdF9yZW9yZGVyKHdvcmQsIHRmX2lkZikpICU+JQ0KICBtdXRhdGUoYXV0aG9yID0gZmFjdG9yKGF1dGhvciwgbGV2ZWxzID0gYygiR2FsaWxlaSwgR2FsaWxlbyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJIdXlnZW5zLCBDaHJpc3RpYWFuIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUZXNsYSwgTmlrb2xhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVpbnN0ZWluLCBBbGJlcnQiKSkpDQpgYGANCg0KYGBge3J9DQoNCnBsb3RfcGh5c2ljcyAlPiUgDQogIGdyb3VwX2J5KGF1dGhvcikgJT4lIA0KICB0b3BfbigxNSwgdGZfaWRmKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUNCiAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIHRmX2lkZikpICU+JQ0KICBnZ3Bsb3QoYWVzKHdvcmQsIHRmX2lkZiwgZmlsbCA9IGF1dGhvcikpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBsYWJzKHggPSBOVUxMLCB5ID0gInRmLWlkZiIpICsNCiAgZmFjZXRfd3JhcCh+YXV0aG9yLCBuY29sID0gMiwgc2NhbGVzID0gImZyZWUiKSArDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCi0tLS0tLQ0KMjgvMDQvMjAyMA0KDQpDaGFwdGVyIDMgDQpXZSB3YW50IHRvIGFuc3dlciBob3cgdG8gcXVhbnRpZnkgd2hhdCBhIGRvY3VtZW50IGlzIGFib3V0DQoNClRvIGFuc3dlciB0aGVzZSBxdWVzdGlvbnMgd2UgdGVzdCB0aGlzIG1ldGhvZDoNCnRmID0gdGVybSBmcmVxdWVuY3kNCmlkZiA9IGludmVyc2UgZG9jdW1lbnQgZnJlcXVlbmN5ID0gZGVjcmVhc2VzIHRoZSB3ZWlnaHQgZm9yIGNvbW1vbmx5IHVzZWQgd29yZHMgYW5kIGluY3JlYXNlcyB0aGUgd2VpZ2h0IGZvciB3b3JkcyB0aGF0IGFyZSBub3QgdXNlZCB2ZXJ5IG11Y2ggaW4gYSBjb2xsZWN0aW9uIG9mIGRvY3VtZW50cw0KDQp0ZiAtIGlkZiA9IFRoZSBzdGF0aXN0aWMgdGYtaWRmIGlzIGludGVuZGVkIHRvIG1lYXN1cmUgaG93IGltcG9ydGFudCBhIHdvcmQgaXMgdG8gYSBkb2N1bWVudCBpbiBhIGNvbGxlY3Rpb24gKG9yIGNvcnB1cykgb2YgZG9jdW1lbnRzLCBmb3IgZXhhbXBsZSwgdG8gb25lIG5vdmVsIGluIGEgY29sbGVjdGlvbiBvZiBub3ZlbHMgb3IgdG8gb25lIHdlYnNpdGUgaW4gYSBjb2xsZWN0aW9uIG9mIHdlYnNpdGVzLg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShqYW5lYXVzdGVucikNCmxpYnJhcnkodGlkeXRleHQpDQpsaWJyYXJ5KHRleHRkYXRhKQ0KDQpib29rX3dvcmRzIDwtIGF1c3Rlbl9ib29rcygpICU+JQ0KICB1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQpICU+JQ0KICBjb3VudChib29rLCB3b3JkLCBzb3J0ID0gVFJVRSkNCg0KdG90YWxfd29yZHMgPC0gYm9va193b3JkcyAlPiUgDQogIGdyb3VwX2J5KGJvb2spICU+JSANCiAgc3VtbWFyaXplKHRvdGFsID0gc3VtKG4pKQ0KDQpib29rX3dvcmRzIDwtIGxlZnRfam9pbihib29rX3dvcmRzLCB0b3RhbF93b3JkcykNCmJvb2tfd29yZHMNCmBgYA0KDQpaaXBm4oCZcyBsYXcgc3RhdGVzIHRoYXQgdGhlIGZyZXF1ZW5jeSB0aGF0IGEgd29yZCBhcHBlYXJzIGlzIGludmVyc2VseSBwcm9wb3J0aW9uYWwgdG8gaXRzIHJhbmsuDQoNCmBgYHtyfQ0KZnJlcV9ieV9yYW5rIDwtIGJvb2tfd29yZHMgJT4lIA0KICBncm91cF9ieShib29rKSAlPiUgDQogIG11dGF0ZShyYW5rID0gcm93X251bWJlcigpLCANCiAgICAgICAgIGB0ZXJtIGZyZXF1ZW5jeWAgPSBuL3RvdGFsKQ0KDQpmcmVxX2J5X3JhbmsNCmBgYA0KDQpgYGB7cn0NCmZyZXFfYnlfcmFuayAlPiUgDQogIGdncGxvdChhZXMocmFuaywgYHRlcm0gZnJlcXVlbmN5YCwgY29sb3IgPSBib29rKSkgKyANCiAgZ2VvbV9saW5lKHNpemUgPSAxLjEsIGFscGhhID0gMC44LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIA0KICBzY2FsZV94X2xvZzEwKCkgKw0KICBzY2FsZV95X2xvZzEwKCkNCg0KYGBgDQoNCmBgYHtyfQ0KcmFua19zdWJzZXQgPC0gZnJlcV9ieV9yYW5rICU+JSANCiAgZmlsdGVyKHJhbmsgPCA1MDAsDQogICAgICAgICByYW5rID4gMTApDQoNCmxtKGxvZzEwKGB0ZXJtIGZyZXF1ZW5jeWApIH4gbG9nMTAocmFuayksIGRhdGEgPSByYW5rX3N1YnNldCkNCmBgYA0KDQoNCmBgYHtyfQ0KZnJlcV9ieV9yYW5rICU+JSANCiAgZ2dwbG90KGFlcyhyYW5rLCBgdGVybSBmcmVxdWVuY3lgLCBjb2xvciA9IGJvb2spKSArIA0KICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAtMC42Miwgc2xvcGUgPSAtMS4xLCBjb2xvciA9ICJncmF5NTAiLCBsaW5ldHlwZSA9IDIpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxLjEsIGFscGhhID0gMC44LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIA0KICBzY2FsZV94X2xvZzEwKCkgKw0KICBzY2FsZV95X2xvZzEwKCkNCmBgYA0KDQoNClRoZSBpZGVhIG9mIHRmLWlkZiBpcyB0byBmaW5kIHRoZSBpbXBvcnRhbnQgd29yZHMgZm9yIHRoZSBjb250ZW50IG9mIGVhY2ggZG9jdW1lbnQgYnkgZGVjcmVhc2luZyB0aGUgd2VpZ2h0IGZvciBjb21tb25seSB1c2VkIHdvcmRzIGFuZCBpbmNyZWFzaW5nIHRoZSB3ZWlnaHQgZm9yIHdvcmRzIHRoYXQgYXJlIG5vdCB1c2VkIHZlcnkgbXVjaCBpbiBhIGNvbGxlY3Rpb24gb3IgY29ycHVzIG9mIGRvY3VtZW50cywgaW4gdGhpcyBjYXNlLCB0aGUgZ3JvdXAgb2YgSmFuZSBBdXN0ZW7igJlzIG5vdmVscyBhcyBhIHdob2xlLiBDYWxjdWxhdGluZyB0Zi1pZGYgYXR0ZW1wdHMgdG8gZmluZCB0aGUgd29yZHMgdGhhdCBhcmUgaW1wb3J0YW50IChpLmUuLCBjb21tb24pIGluIGEgdGV4dCwgYnV0IG5vdCB0b28gY29tbW9uDQoNCmBgYHtyfQ0KYm9va193b3JkcyA8LSBib29rX3dvcmRzICU+JQ0KICBiaW5kX3RmX2lkZih3b3JkLCBib29rLCBuKQ0KDQpib29rX3dvcmRzDQpgYGANCg0KYGBge3J9DQpib29rX3dvcmRzICU+JQ0KICBzZWxlY3QoLXRvdGFsKSAlPiUNCiAgYXJyYW5nZShkZXNjKHRmX2lkZikpDQpgYGANCmBgYHtyfQ0KYm9va193b3JkcyAlPiUNCiAgYXJyYW5nZShkZXNjKHRmX2lkZikpICU+JQ0KICBtdXRhdGUod29yZCA9IGZhY3Rvcih3b3JkLCBsZXZlbHMgPSByZXYodW5pcXVlKHdvcmQpKSkpDQpgYGANCg0KDQpgYGB7cn0NCmJvb2tfd29yZHMgJT4lDQogIGFycmFuZ2UoZGVzYyh0Zl9pZGYpKSAlPiUNCiAgbXV0YXRlKHdvcmQgPSBmYWN0b3Iod29yZCwgbGV2ZWxzID0gcmV2KHVuaXF1ZSh3b3JkKSkpKSAlPiUgDQogIGdyb3VwX2J5KGJvb2spICU+JSANCiAgdG9wX24oMTUpICU+JSANCiAgdW5ncm91cCgpDQpgYGANCg0KYGBge3J9DQpib29rX3dvcmRzICU+JQ0KICBhcnJhbmdlKGRlc2ModGZfaWRmKSkgJT4lDQogIG11dGF0ZSh3b3JkID0gZmFjdG9yKHdvcmQsIGxldmVscyA9IHJldih1bmlxdWUod29yZCkpKSkgJT4lIA0KICBncm91cF9ieShib29rKSAlPiUgDQogIHRvcF9uKDE1KSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUNCiAgZ2dwbG90KGFlcyh3b3JkLCB0Zl9pZGYsIGZpbGwgPSBib29rKSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGxhYnMoeCA9IE5VTEwsIHkgPSAidGYtaWRmIikgKw0KICBmYWNldF93cmFwKH5ib29rLCBuY29sID0gMiwgc2NhbGVzID0gImZyZWUiKSArDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQpTdGlsbCBhbGwgcHJvcGVyIG5vdW5zIGluIEZpZ3VyZSAzLjQhIFRoZXNlIHdvcmRzIGFyZSwgYXMgbWVhc3VyZWQgYnkgdGYtaWRmLCB0aGUgbW9zdCBpbXBvcnRhbnQgdG8gZWFjaCBub3ZlbCBhbmQgbW9zdCByZWFkZXJzIHdvdWxkIGxpa2VseSBhZ3JlZS4gV2hhdCBtZWFzdXJpbmcgdGYtaWRmIGhhcyBkb25lIGhlcmUgaXMgc2hvdyB1cyB0aGF0IEphbmUgQXVzdGVuIHVzZWQgc2ltaWxhciBsYW5ndWFnZSBhY3Jvc3MgaGVyIHNpeCBub3ZlbHMsIGFuZCB3aGF0IGRpc3Rpbmd1aXNoZXMgb25lIG5vdmVsIGZyb20gdGhlIHJlc3Qgd2l0aGluIHRoZSBjb2xsZWN0aW9uIG9mIGhlciB3b3JrcyBhcmUgdGhlIHByb3BlciBub3VucywgdGhlIG5hbWVzIG9mIHBlb3BsZSBhbmQgcGxhY2VzLiBUaGlzIGlzIHRoZSBwb2ludCBvZiB0Zi1pZGY7IGl0IGlkZW50aWZpZXMgd29yZHMgdGhhdCBhcmUgaW1wb3J0YW50IHRvIG9uZSBkb2N1bWVudCB3aXRoaW4gYSBjb2xsZWN0aW9uIG9mIGRvY3VtZW50cy4NCg0KDQoNCmBgYHtyfQ0Kc3VwcHJlc3NNZXNzYWdlcyh7DQogIGlmKCFyZXF1aXJlKGd1dGVuYmVyZ3IpKQ0KICAgIGluc3RhbGwucGFja2FnZXMoImd1dGVuYmVyZ3IiLCByZXBvcyA9ICJodHRwOi8vY3Jhbi51cy5yLXByb2plY3Qub3JnIikNCiAgbGlicmFyeShndXRlbmJlcmdyKQ0KICANCn0pDQpwaHlzaWNzIDwtIGd1dGVuYmVyZ19kb3dubG9hZChjKDM3NzI5LCAxNDcyNSwgMTM0NzYsIDMwMTU1KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhX2ZpZWxkcyA9ICJhdXRob3IiKQ0KYGBgDQoNCg0KIyBUbyBjYWxjdWxhdGUgYW5kIHZpc3VsaXplIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB3b3JkcyBpbiBteSB0ZXh0IGRhdGFzZXQNClRva2VuaXppbmcgYnkgbi1ncmFtDQoNCldlIGRvIHRoaXMgYnkgYWRkaW5nIHRoZSB0b2tlbiA9ICJuZ3JhbXMiIG9wdGlvbiB0byB1bm5lc3RfdG9rZW5zKCksIGFuZCBzZXR0aW5nIG4gdG8gdGhlIG51bWJlciBvZiB3b3JkcyB3ZSB3aXNoIHRvIGNhcHR1cmUgaW4gZWFjaCBuLWdyYW0uIFdoZW4gd2Ugc2V0IG4gdG8gMiwgd2UgYXJlIGV4YW1pbmluZyBwYWlycyBvZiB0d28gY29uc2VjdXRpdmUgd29yZHMsIG9mdGVuIGNhbGxlZCDigJxiaWdyYW1z4oCdOg0KYGBge3J9DQphdXN0ZW5fYmlncmFtcyA8LSBhdXN0ZW5fYm9va3MoKSAlPiUNCiAgdW5uZXN0X3Rva2VucyhiaWdyYW0sIHRleHQsIHRva2VuID0gIm5ncmFtcyIsIG4gPSAyKQ0KDQphdXN0ZW5fYmlncmFtcw0KYGBgDQoNCiBDb3VudGluZyBhbmQgZmlsdGVyaW5nIG4tZ3JhbXMNCmBgYHtyfQ0KYXVzdGVuX2JpZ3JhbXMgJT4lDQogIGNvdW50KGJpZ3JhbSwgc29ydCA9IFRSVUUpDQpgYGANCg0KQXMgb25lIG1pZ2h0IGV4cGVjdCwgYSBsb3Qgb2YgdGhlIG1vc3QgY29tbW9uIGJpZ3JhbXMgYXJlIHBhaXJzIG9mIGNvbW1vbiAodW5pbnRlcmVzdGluZykgd29yZHMsIHN1Y2ggYXMgb2YgdGhlIGFuZCB0byBiZTogd2hhdCB3ZSBjYWxsIOKAnHN0b3Atd29yZHPigJ0gKHNlZSBDaGFwdGVyIDEpLiBUaGlzIGlzIGEgdXNlZnVsIHRpbWUgdG8gdXNlIHRpZHly4oCZcyBzZXBhcmF0ZSgpLCB3aGljaCBzcGxpdHMgYSBjb2x1bW4gaW50byBtdWx0aXBsZSBiYXNlZCBvbiBhIGRlbGltaXRlci4gVGhpcyBsZXRzIHVzIHNlcGFyYXRlIGl0IGludG8gdHdvIGNvbHVtbnMsIOKAnHdvcmQx4oCdIGFuZCDigJx3b3JkMuKAnSwgYXQgd2hpY2ggcG9pbnQgd2UgY2FuIHJlbW92ZSBjYXNlcyB3aGVyZSBlaXRoZXIgaXMgYSBzdG9wLXdvcmQuDQoNCmBgYHtyfQ0KYmlncmFtc19zZXBhcmF0ZWQgPC0gYXVzdGVuX2JpZ3JhbXMgJT4lDQogIHNlcGFyYXRlKGJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKQ0KDQpiaWdyYW1zX2ZpbHRlcmVkIDwtIGJpZ3JhbXNfc2VwYXJhdGVkICU+JQ0KICBmaWx0ZXIoIXdvcmQxICVpbiUgc3RvcF93b3JkcyR3b3JkKSAlPiUNCiAgZmlsdGVyKCF3b3JkMiAlaW4lIHN0b3Bfd29yZHMkd29yZCkNCg0KIyBuZXcgYmlncmFtIGNvdW50czoNCmJpZ3JhbV9jb3VudHMgPC0gYmlncmFtc19maWx0ZXJlZCAlPiUgDQogIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpDQoNCmJpZ3JhbV9jb3VudHMNCmBgYA0KDQoNCmBgYHtyfQ0KYmlncmFtc191bml0ZWQgPC0gYmlncmFtc19maWx0ZXJlZCAlPiUNCiAgdW5pdGUoYmlncmFtLCB3b3JkMSwgd29yZDIsIHNlcCA9ICIgIikNCg0KYmlncmFtc191bml0ZWQNCmBgYA0KDQpJbiBvdGhlciBhbmFseXNlcyB5b3UgbWF5IGJlIGludGVyZXN0ZWQgaW4gdGhlIG1vc3QgY29tbW9uIHRyaWdyYW1zLCB3aGljaCBhcmUgY29uc2VjdXRpdmUgc2VxdWVuY2VzIG9mIDMgd29yZHMuIFdlIGNhbiBmaW5kIHRoaXMgYnkgc2V0dGluZyBuID0gMzoNCg0KYGBge3J9DQphdXN0ZW5fYm9va3MoKSAlPiUNCiAgdW5uZXN0X3Rva2Vucyh0cmlncmFtLCB0ZXh0LCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMykgJT4lDQogIHNlcGFyYXRlKHRyaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiwgIndvcmQzIiksIHNlcCA9ICIgIikgJT4lDQogIGZpbHRlcighd29yZDEgJWluJSBzdG9wX3dvcmRzJHdvcmQsDQogICAgICAgICAhd29yZDIgJWluJSBzdG9wX3dvcmRzJHdvcmQsDQogICAgICAgICAhd29yZDMgJWluJSBzdG9wX3dvcmRzJHdvcmQpICU+JQ0KICBjb3VudCh3b3JkMSwgd29yZDIsIHdvcmQzLCBzb3J0ID0gVFJVRSkNCg0KYGBgDQoNCg0KYGBge3J9DQpiaWdyYW1zX2ZpbHRlcmVkICU+JQ0KICBmaWx0ZXIod29yZDIgPT0gInN0cmVldCIpICU+JQ0KICBjb3VudChib29rLCB3b3JkMSwgc29ydCA9IFRSVUUpDQpgYGANCg0KQSBiaWdyYW0gY2FuIGFsc28gYmUgdHJlYXRlZCBhcyBhIHRlcm0gaW4gYSBkb2N1bWVudCBpbiB0aGUgc2FtZSB3YXkgdGhhdCB3ZSB0cmVhdGVkIGluZGl2aWR1YWwgd29yZHMuIEZvciBleGFtcGxlLCB3ZSBjYW4gbG9vayBhdCB0aGUgdGYtaWRmIChDaGFwdGVyIDMpIG9mIGJpZ3JhbXMgYWNyb3NzIEF1c3RlbiBub3ZlbHMuIFRoZXNlIHRmLWlkZiB2YWx1ZXMgY2FuIGJlIHZpc3VhbGl6ZWQgd2l0aGluIGVhY2ggYm9vaywganVzdCBhcyB3ZSBkaWQgZm9yIHdvcmRzIChGaWd1cmUgNC4xKS4NCg0KYGBge3J9DQpiaWdyYW1fdGZfaWRmIDwtIGJpZ3JhbXNfdW5pdGVkICU+JQ0KICBjb3VudChib29rLCBiaWdyYW0pICU+JQ0KICBiaW5kX3RmX2lkZihiaWdyYW0sIGJvb2ssIG4pICU+JQ0KICBhcnJhbmdlKGRlc2ModGZfaWRmKSkNCg0KYmlncmFtX3RmX2lkZg0KYGBgDQoNCmBgYHtyfQ0Kbm90X3dvcmRzIDwtIGJpZ3JhbXNfc2VwYXJhdGVkICU+JQ0KICBmaWx0ZXIod29yZDEgPT0gIm5vdCIpICU+JQ0KICBpbm5lcl9qb2luKEFGSU5OLCBieSA9IGMod29yZDIgPSAid29yZCIpKSAlPiUNCiAgY291bnQod29yZDIsIHZhbHVlLCBzb3J0ID0gVFJVRSkNCg0Kbm90X3dvcmRzDQpgYGANCg0KDQoNCmBgYHtyfQ0Kbm90X3dvcmRzICU+JQ0KICBtdXRhdGUoY29udHJpYnV0aW9uID0gbiAqIHZhbHVlKSAlPiUNCiAgYXJyYW5nZShkZXNjKGFicyhjb250cmlidXRpb24pKSkgJT4lDQogIGhlYWQoMjApICU+JQ0KICBtdXRhdGUod29yZDIgPSByZW9yZGVyKHdvcmQyLCBjb250cmlidXRpb24pKSAlPiUNCiAgZ2dwbG90KGFlcyh3b3JkMiwgbiAqIHZhbHVlLCBmaWxsID0gbiAqIHZhbHVlID4gMCkpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICB4bGFiKCJXb3JkcyBwcmVjZWRlZCBieSBcIm5vdFwiIikgKw0KICB5bGFiKCJTZW50aW1lbnQgdmFsdWUgKiBudW1iZXIgb2Ygb2NjdXJyZW5jZXMiKSArDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCmBgYHtyfQ0KbmVnYXRpb25fd29yZHMgPC0gYygibm90IiwgIm5vIiwgIm5ldmVyIiwgIndpdGhvdXQiKQ0KQUZJTk4gPC0gZ2V0X3NlbnRpbWVudHMoImFmaW5uIikNCm5lZ2F0ZWRfd29yZHMgPC0gYmlncmFtc19zZXBhcmF0ZWQgJT4lDQogIGZpbHRlcih3b3JkMSAlaW4lIG5lZ2F0aW9uX3dvcmRzKSAlPiUNCiAgaW5uZXJfam9pbihBRklOTiwgYnkgPSBjKHdvcmQyID0gIndvcmQiKSkgJT4lDQogIGNvdW50KHdvcmQxLCB3b3JkMiwgdmFsdWUsIHNvcnQgPSBUUlVFKQ0KbmVnYXRlZF93b3Jkcw0KYGBgDQpgYGB7cn0NCm5lZ2F0ZWRfd29yZHMgJT4lIHNlbGVjdCh3b3JkMSkgJT4lIGRpc3RpbmN0KCkNCmBgYA0KDQoNCg0KYGBge3J9DQpuZWdhdGVkX3dvcmRzICU+JQ0KICBtdXRhdGUod29yZDEgPSBmYWN0b3Iod29yZDEsIGxldmVscyA9IHVuaXF1ZSh3b3JkMSkpKSAlPiUgDQogIGdyb3VwX2J5KHdvcmQxKSAlPiUNCiAgdG9wX24oMTIpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIG11dGF0ZShjb250cmlidXRpb24gPSBuICogdmFsdWUpICU+JQ0KICBtdXRhdGUod29yZDIgPSByZW9yZGVyX3dpdGhpbih3b3JkMiwgY29udHJpYnV0aW9uLCB3b3JkMSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHdvcmQyLCBjb250cmlidXRpb24sIGZpbGwgPSBjb250cmlidXRpb24gPiAwKSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIHhsYWIoIldvcmRzIHByZWNlZGVkIGJ5IFwibm90XCIiKSArDQogIHlsYWIoIlNlbnRpbWVudCB2YWx1ZSAqIG51bWJlciBvZiBvY2N1cnJlbmNlcyIpICsNCiAgZmFjZXRfd3JhcCh+IHdvcmQxLCBuY29sID0gMiwgc2NhbGVzID0gImZyZWUiLCBkaXIgPSAidiIpICsNCiAgY29vcmRfZmxpcCgpICsgDQogIHNjYWxlX3hfcmVvcmRlcmVkKCkNCmBgYA0KDQoNCi0tLS0tLS0tLQ0KMjAvMDQvMjAyMA0KDQpgYGB7cn0NCnN1cHByZXNzTWVzc2FnZXMoew0KICBpZighcmVxdWlyZSh0b3BpY21vZGVscykpDQogICAgaW5zdGFsbC5wYWNrYWdlcygidG9waWNtb2RlbHMiLCByZXBvcyA9ICJodHRwOi8vY3Jhbi51cy5yLXByb2plY3Qub3JnIikNCiAgbGlicmFyeSh0b3BpY21vZGVscykNCiAgDQp9KQ0KDQoNCmRhdGEoIkFzc29jaWF0ZWRQcmVzcyIpDQpBc3NvY2lhdGVkUHJlc3MNCmBgYA0KDQpgYGB7cn0NCiMgc2V0IGEgc2VlZCBzbyB0aGF0IHRoZSBvdXRwdXQgb2YgdGhlIG1vZGVsIGlzIHByZWRpY3RhYmxlDQphcF9sZGEgPC0gTERBKEFzc29jaWF0ZWRQcmVzcywgayA9IDIsIGNvbnRyb2wgPSBsaXN0KHNlZWQgPSAxMjM0KSkNCmFwX2xkYQ0KYGBgDQoNCg0KYGBge3J9DQpzdXBwcmVzc01lc3NhZ2VzKHsNCiAgaWYoIXJlcXVpcmUodGlkeXRleHQpKQ0KICAgIGluc3RhbGwucGFja2FnZXMoInRpZHl0ZXh0IiwgcmVwb3MgPSAiaHR0cDovL2NyYW4udXMuci1wcm9qZWN0Lm9yZyIpDQogIGxpYnJhcnkodGlkeXRleHQpDQp9KQ0KDQoNCmFwX3RvcGljcyA8LSB0aWR5KGFwX2xkYSwgbWF0cml4ID0gImJldGEiKQ0KYXBfdG9waWNzDQpgYGANCg0KYXBfdG9wX3Rlcm1zIDwtIGFwX3RvcGljcyAlPiUNCiAgZ3JvdXBfYnkodG9waWMpICU+JQ0KICB0b3BfbigxMCwgYmV0YSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgYXJyYW5nZSh0b3BpYywgLWJldGEpDQoNCmFwX3RvcF90ZXJtcyAlPiUNCiAgbXV0YXRlKHRlcm0gPSByZW9yZGVyX3dpdGhpbih0ZXJtLCBiZXRhLCB0b3BpYykpICU+JQ0KICBnZ3Bsb3QoYWVzKHRlcm0sIGJldGEsIGZpbGwgPSBmYWN0b3IodG9waWMpKSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGZhY2V0X3dyYXAofiB0b3BpYywgc2NhbGVzID0gImZyZWUiKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIHNjYWxlX3hfcmVvcmRlcmVkKCkNCiAgDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQoNCmFwX3RvcF90ZXJtcyA8LSBhcF90b3BpY3MgJT4lDQogIGdyb3VwX2J5KHRvcGljKSAlPiUNCiAgdG9wX24oMTAsYmV0YSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgYXJyYW5nZSh0b3BpYywgLWJldGEpDQphcF90b3BfdGVybXMNCg0KYXBfdG9wX3Rlcm1zICU+JQ0KICBtdXRhdGUodGVybSA9IHJlb3JkZXJfd2l0aGluKHRlcm0sIGJldGEsIHRvcGljKSkgJT4lDQogIGdncGxvdChhZXModGVybSwgYmV0YSwgZmlsbCA9IGZhY3Rvcih0b3BpYykpKSArDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZmFjZXRfd3JhcCh+IHRvcGljLCBzY2FsZXMgPSAiZnJlZSIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgc2NhbGVfeF9yZW9yZGVyZWQoKQ0KYGBgDQoNCg0KDQo=